﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpSetupWorkPlaneByPickAction.h"

#include "IPickFilter.h"
#include "IUiView.h"
#include "IReferencePlane.h"
#include "IGraphicsBRepFace.h"
#include "IUserTransaction.h"
#include "IModelView.h"
#include "IPlane.h"
#include "IElement.h"
#include "IInstance.h"
#include "ILevel.h"
#include "Vector3dUtils.h"
#include "LevelUtils.h"
#include "IGraphicsLine.h"
#include "IPickNodeReferenceAction.h"
#include "GbmpModelViewTypes.h"
#include "IPickTarget.h"
#include "IDocument.h"
#include "IRegenerator.h"

#include "GbmpModelViewUtil.h"
#include "GbmpSwitchViewActionRequest.h"

#include "ContainerUtil.h"
#include "IGraphicsNodeReference.h"
#include "GraphicsNodeUtils.h"
#include "IElementModelShape.h"
#include "IGraphicsElementShape.h"
#include "IElementStatus.h"
#include "IHighlights.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace
{
    class SetupWorkPlanePickFaceFilter : public gcmp::IPickFilter
    {
    public:
        SetupWorkPlanePickFaceFilter(const gcmp::IDocument* pDoc, const gcmp::IModelView* pModelView);
        ~SetupWorkPlanePickFaceFilter();
        virtual bool AllowElement(const gcmp::ElementId& elementID) const override;
        virtual bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const override;
        virtual bool SetPickTargetOption(gcmp::IPickTarget* pickTarget) override;
        void SetAllowFaceNormal(gcmp::Vector3d allowedFaceNormal);

        const IDocument* GetDocument() const { return m_pDoc; }
    private:
        const IDocument* m_pDoc;
        gcmp::Vector3d m_allowedFaceNormal;
        const gcmp::IModelView* m_pModelView;
    };

    SetupWorkPlanePickFaceFilter::SetupWorkPlanePickFaceFilter(const gcmp::IDocument* pDoc, const gcmp::IModelView* pModelView)
        : m_pDoc(pDoc)
        , m_allowedFaceNormal(gcmp::Vector3d::CreateZeroVector3d())
        , m_pModelView(pModelView)
    {

    }

    SetupWorkPlanePickFaceFilter::~SetupWorkPlanePickFaceFilter()
    {
    }

    bool SetupWorkPlanePickFaceFilter::AllowElement(const ElementId& elementID) const
    {
        return true;
    }

    bool SetupWorkPlanePickFaceFilter::AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const
    {
        const IElement* pElement = GetDocument()->GetElement(nodeReference.GetElementId());
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pElement, L"GNodeReference指向的构件找不到！",L"GDMPLab",L"2024-03-30");

        const IGraphicsElementShape* pModelGRep = pElement->GetElementModelShape()->GetGraphicsElementShape();
        //没有模型GRep
        if (!pModelGRep)
            return false;
        
        const IGraphicsNode* pNode = nodeReference.GetUntransformedIntersectGraphicsNode();
        const IGraphicsBRepFace* pGFace = dynamic_cast<const IGraphicsBRepFace*>(pNode);
        //所有的平面都可以拾取
        if (pGFace && pGFace->IsPlanar())
        {
            Vector3d normal = pGFace->GetNormal();
            if (m_allowedFaceNormal.IsZero())
            {
                return true;
            }
            else if (Vector3dUtils::IsParallel(m_allowedFaceNormal, normal))
            {
                return true;
            }
        }

        return false;
    }

    bool SetupWorkPlanePickFaceFilter::SetPickTargetOption(gcmp::IPickTarget* pickTarget)
    {
        pickTarget->DisableAll();
        pickTarget->EnableFace();
        pickTarget->EnableCurve();
        return true;
    }

    void SetupWorkPlanePickFaceFilter::SetAllowFaceNormal(gcmp::Vector3d allowedFaceNormal)
    {
        m_allowedFaceNormal = allowedFaceNormal;
    }
}
///////////////////////////////////////////////////////////////////////////////////////
namespace gcmp
{
    GbmpSetupWorkPlaneByPickAction::GbmpSetupWorkPlaneByPickAction()
        : m_isUserCancelled(false)
    {
    }


    GbmpSetupWorkPlaneByPickAction::~GbmpSetupWorkPlaneByPickAction()
    {
    }

    void GbmpSetupWorkPlaneByPickAction::InitAction(IUiView* pCurrentView)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView != nullptr, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");

        EnterPickFaceState(pCurrentView);
    }

    void GbmpSetupWorkPlaneByPickAction::OnChildActionFinished(IUiView* pCurrentView, const ActionOutput& childActionReturnParam)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView != nullptr, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");
        gcmp::IDocument* pDocument = GetDoc();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDocument, L"doc为空",L"GDMPLab",L"2024-03-30");

        if (!m_isUserCancelled)
        {
            ElementId pickedElementID = m_nodeReferences.at(0)->GetElementId();
            gcmp::IElement* pElement = pDocument->GetElement(pickedElementID);
            DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"ele为空",L"GDMPLab",L"2024-03-30");

            OwnerPtr<IUserTransaction> opUt = IUserTransaction::Create(pDocument, GBMP_TR(L"为设置工作平面添加参照平面"));
            DBG_WARN_AND_RETURN_VOID_UNLESS(opUt.get(), L"opUt为空",L"GDMPLab",L"2024-03-30");
            {
                // 由拾取到的面创建的参照平面
                IReferencePlane* createdRefPlane = nullptr;
                // 1.拾取的是标高
                if(gcmp::ILevel* pElevation = LevelUtils::GetLevelById(pDocument, pElement->GetElementId()))
                {
                    SetElevationToModelView(pCurrentView, pElevation);
                }

                // 2.拾取的是其他面
                OwnerPtr<GraphicsNodeAndTransform> pTransformNode = m_nodeReferences.at(0)->GetIntersectGraphicsNode();
                if (pTransformNode != nullptr)
                {
                    const IGraphicsBRepFace *pPickedGFace = dynamic_cast<const IGraphicsBRepFace *>(pTransformNode->m_wpGraphicsNode.Get());
                    if (pPickedGFace != nullptr && pPickedGFace->IsPlanar())
                    {
                        //用GFace创建一个ReferencePlane
                        createdRefPlane = IReferencePlane::CreateByGraphicsNodeReference(pDocument, *m_nodeReferences.at(0));
                        createdRefPlane->SetReferencePlaneType(ReferencePlaneType::Aided);
                        IElementStatus* pStatus = createdRefPlane->GetStatus();
                        DBG_WARN_AND_RETURN_VOID_UNLESS(pStatus, L"无法获取状态集",L"GDMPLab",L"2024-03-30");
                        pStatus->SetIsVisible(false);
                        createdRefPlane->GetDocument()->GetRegenerator()->RegenerateElement(createdRefPlane->GetElementId());//必须调用，用来创建IGraphicsPlane
                    }
                }

                // 3.将拾取或创建的ReferencePlane，赋给View的WorkPlane中的参照平面引用
                if (gcmp::IReferencePlane* pPickedRefPlane = dynamic_cast<gcmp::IReferencePlane*>(pElement))
                {
                    SetWorkPlaneByReferencePlane(pCurrentView->GetModelView(), pPickedRefPlane);
                }
                else if (createdRefPlane)
                {
                    SetWorkPlaneByReferencePlane(pCurrentView->GetModelView(), createdRefPlane);
                }
            }
            opUt->Commit();
            IHighlights::Get()->Clear();
            MarkFinishStatus(ActionFinishStatus::Successful);
        }
        else
        {
            MarkFinishStatus(ActionFinishStatus::Cancelled);
        }
    }

    void GbmpSetupWorkPlaneByPickAction::SetElevationToModelView(IUiView* pCurrentView, const gcmp::ILevel* pNewElevation)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView != nullptr, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");
        gcmp::IDocument* pDocument = GetDoc();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDocument, L"document为空",L"GDMPLab",L"2024-03-30");

        const IPlane* pPlane = pNewElevation->GetReferencePlane();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pPlane, L"upDirection为空",L"GDMPLab",L"2024-03-30");
        IModelView* modelView = pCurrentView->GetModelView();
        DBG_WARN_AND_RETURN_VOID_UNLESS(modelView, L"modelView为空",L"GDMPLab",L"2024-03-30");
        const std::wstring& viewType = modelView->GetUserDataId();


        if (viewType == View3DViewType)
        {
            OwnerPtr<IPlane> opPlane = IPlane::Create(pPlane->GetOrigin(), pPlane->GetDirectionU(), pPlane->GetDirectionV());
            modelView->SetWorkPlane(opPlane.get());

            GbmpModelViewUtil::SetWorkPlaneName(modelView, pNewElevation->GetName());
        }
        else
        {
            Vector3d dirZ = Vector3d::UnitZ;
            //检查是否需要切换视图
            ElementId viewIdToSwitch = GetViewToSwitch(pCurrentView->GetModelView(), dirZ);
            if (viewIdToSwitch.IsValid() == false)
            {
                return;
            }

            PostRequest(NEW_AS_OWNER_PTR(GbmpSwitchViewActionRequest, viewIdToSwitch));

            modelView = quick_cast<IModelView>(pDocument->GetElement(viewIdToSwitch));
            DBG_WARN_AND_RETURN_VOID_UNLESS(modelView, L"ModelView为空",L"GDMPLab",L"2024-03-30");
            OwnerPtr<IPlane> opPlane = IPlane::Create(pPlane->GetOrigin(), pPlane->GetDirectionU(), pPlane->GetDirectionV());
            modelView->SetWorkPlane(opPlane.get());
            GbmpModelViewUtil::SetWorkPlaneName(modelView, pNewElevation->GetName());
        }
    }

    void GbmpSetupWorkPlaneByPickAction::EnterPickFaceState(IUiView* pCurrentView)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView != nullptr, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");

        //可以选实例，参照平面，标高，实体上的面
        m_isUserCancelled = false;
        SetPromptMessage(GBMP_TR(L"拾取面或参考平面做为工作平面："));

        IModelView* pModelView = pCurrentView->GetModelView();
        Vector3d workPlaneNormal = Vector3d::Zero;
        if (pModelView->GetViewType() != BuiltInViewType::ThreeDimensional)
        {
            workPlaneNormal = pModelView->GetViewDirection();
        }
        DBG_WARN_AND_RETURN_VOID_UNLESS(pModelView != nullptr, L"modelView为空",L"GDMPLab",L"2024-03-30");
        gcmp::OwnerPtr<SetupWorkPlanePickFaceFilter> upSetupWorkPlanePickFaceFilter = NEW_AS_OWNER_PTR(SetupWorkPlanePickFaceFilter, GetDoc(), pModelView);
        upSetupWorkPlanePickFaceFilter->SetAllowFaceNormal(workPlaneNormal);
        PickNodeReferenceOption option;
        option.SetIsPickingHighlightOnlyGraphicsNodeAllowed(true);
        OwnerPtr<IAction> upPickNodeReferenceAction = IPickNodeReferenceAction::Create(
            PickNodeReferenceExchangeData(true,&m_nodeReferences,&m_isUserCancelled),
            TransferOwnership(upSetupWorkPlanePickFaceFilter),
            option);
        
        StartChildAction(TransferOwnership(upPickNodeReferenceAction));
    }

}


